
/*
 * Copyright 2014 Щекотова Ангелина Александровна.
 * Этот файл — часть J4JClientServer Framework
 * J4JClientServer Framework - свободная программа: вы можете перераспространять ее и/или изменять
 * ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде, в каком
 * она была опубликована Фондом свободного программного обеспечения; либо
 * версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
 *
 * J4JClientServer Framework распространяется в надежде, что она будет полезной,
 * но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
 * или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
 * общественной лицензии GNU.
 *
 * Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
 * вместе с этой программой. Если это не так, см.
 * <http://www.gnu.org/licenses/>.
 */

package ru.java4java.utils;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.Vector;

public final class ClassUtils {

    private static final Logger log = Logger.getLogger(ClassUtils.class);

    public static <T> T createObject(Class<T> clazz, Object... params) {
        try {
            return clazz.getConstructor(getClasses(params)).newInstance(params);
        } catch (InstantiationException e) {
            log.error("InstantiationException", e);
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            log.error("IllegalAccessException", e);
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            log.error("InvocationTargetException", e);
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            log.error("NoSuchMethodException", e);
            throw new RuntimeException(e);
        }
    }

    public static Class[] getClasses(Object... objects) {
        Class[] classes = new Class[objects.length];
        for (int i = 0; i < classes.length; i++) {
            classes[i] = objects[i].getClass();
        }
        return classes;
    }

    public static String[] getClassNames(Object... objects) {
        int length = objects.length;
        String[] names = new String[length];
        for (int i = 0; i < length; i++) {
            names[i] = objects[i].getClass().getSimpleName();
        }
        return names;
    }

    public Field getField(Class<?> clazz, String fieldName) {
        Field field = null;
        if (StringUtils.isNotEmpty(fieldName)) {
            try {
                field = clazz.getField(fieldName);
            } catch (SecurityException e) {
                throw new RuntimeException(e);
            } catch (NoSuchFieldException e) {
                if (clazz.getSuperclass() != null) {
                    field = getField(clazz.getSuperclass(), fieldName);
                }
            }
        }
        return field;
    }

    public Field getFieldUnsafe(Class<?> clazz, String fieldName) {
        Field field = null;
        if (StringUtils.isNotEmpty(fieldName)) {
            try {
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
            } catch (SecurityException e) {
                throw new RuntimeException(e);
            } catch (NoSuchFieldException e) {
                if (clazz.getSuperclass() != null) {
                    field = getFieldUnsafe(clazz.getSuperclass(), fieldName);
                }
            }
        }
        return field;
    }

    public static String getCurrentMethodName() {
        return getMethodName(4);
    }

    public static String getMethodName(final int depth) {
        if (getSTE(depth) != null) {
            return getSTE(depth).getMethodName();
        } else return "";
    }

    public static StackTraceElement getSTE(final int depth) {
        int threadLength = Thread.currentThread().getStackTrace().length;
        if (depth < threadLength && depth >= 0) {
            final StackTraceElement ste = Thread.currentThread().getStackTrace()[depth];
            return ste;
        } else {
            return null;
        }
    }

    public static Vector<Class> getAllInterfaces(Class<?> clazz) {
        Vector<Class> result = new Vector<>();
        Class<?>[] interfaces = clazz.getInterfaces();
        Collections.addAll(result, interfaces);
        Class superClass = clazz.getSuperclass();
        if (superClass != null) {
            result.addAll(getAllInterfaces(superClass));
        }
        return result;
    }

    public static boolean classHaveInterface(Class<?> clazz, Class<?> interfaceClass) {
        Class<?>[] interfaces = clazz.getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            if (interfaces[i].equals(interfaceClass)) return true;
        }
        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null) {
            return classHaveInterface(superClass, interfaceClass);
        } else {
            return false;
        }
    }

}
